package cpa.prowon.gui;

import java.util.Properties;
import java.util.ResourceBundle;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import cpa.prowon.Application;
import cpa.prowon.gui.util.ImageUtil;

/**
 * The image information window is used to edit the IPTC infomration. It's
 * an application modal window, providing all input fields. This class has
 * to be subclassed for different purposes, like editing a single or multiple
 * images at the same time
 * @author Chris
 */
abstract class ImageInformationWindow {

	// Properties
	protected MainWindow mainWindow;
	protected ResourceBundle rb;
	protected String iptc_title;
	protected Text field_title;
	protected String iptc_gallery;
	protected Text field_gallery;
	protected String iptc_description;
	protected Text field_description;
	protected String iptc_keywords;
	protected Text field_keywords;
	protected String iptc_city;
	protected Text field_city;
	protected String iptc_state;
	protected Text field_state;
	protected String iptc_country;
	protected Text field_country;
	protected String iptc_category;
	protected Text field_category;
	protected String iptc_copyright;
	protected Text field_copyright;
	protected String iptc_date;
	protected Text field_date;
	protected String iptc_supplementalcategories;
	protected Text field_supplementalcategories;
	protected Shell shell;
	
	// Abstract methods
	abstract protected String getTitle();
	abstract protected String getGallery();
	abstract protected String getDescription();
	abstract protected String getKeywords();
	abstract protected String getCity();
	abstract protected String getProvinceState();
	abstract protected String getCountry();
	abstract protected String getCategory();
	abstract protected String getCopyright();
	abstract protected String getDate();
	abstract protected String getOriginatingProgram();
	abstract protected String getSupplementalCategories();
	abstract protected void setTitle(String value);
	abstract protected void setGallery(String value);
	abstract protected void setDescription(String value);
	abstract protected void setKeywords(String value);
	abstract protected void setCity(String value);
	abstract protected void setProvinceState(String value);
	abstract protected void setCountry(String value);
	abstract protected void setCategory(String value);
	abstract protected void setCopyright(String value);
	abstract protected void setDate(String value);
	abstract protected void setOriginatingProgram(String value);
	abstract protected void setSupplementalCategories(String value);
	abstract protected void save();
	abstract protected int getGroupColumnsNumber();
	
	
	/**
	 * Create an additional widget if needed
	 * @param Composite The parent of the additional widget
	 * @author Chris
	 */
	protected void createAdditionalWidget(Composite parent) {
	}

	/**
	 * Property name constant for the x value of the window
	 * @return The property name for the x value of the window
	 * @author Chris
	 */
	protected static final String PROPNAME_X = "prowon.ImageInformationWindow.x";

	/**
	 * Property name constant for the y value of the window
	 * @return The property name for the y value of the window
	 * @author Chris
	 */
	protected static final String PROPNAME_Y = "prowon.ImageInformationWindow.y";

	/**
	 * Constructor
	 * @author Chris
	 */
	protected ImageInformationWindow() {
		
		// Get the resource bundle
		rb = Application.getInstance().getResourceBundle();
		
	}

	/**
	 * Configure the shell
	 * @author Chris
	 */
	protected void configureShell() {
		
		// Configure depending on the last remembered information
		Properties appProps = Application.getInstance().getApplicationProperties();
		
		// Set the location
		String x = appProps.getProperty(ImageInformationWindow.PROPNAME_X, "");
		String y = appProps.getProperty(ImageInformationWindow.PROPNAME_Y, "");
		if (x != "" & y != "") {
			int ix = Integer.parseInt(x);
			int iy = Integer.parseInt(y);
			shell.setLocation(ix, iy);
		}
		
	}
	
	/**
	 * Open the window
	 * @author Chris
	 */
	public void open(boolean readOnly) {

		// Create the contents
		shell = createContents(readOnly);
		
		// Configure the shell
		configureShell();
		
		// Open the shell
		shell.pack();
		shell.open ();

		// Dispose the shell
		Display display = shell.getDisplay();
		while(!shell.isDisposed()){
			if(!display.readAndDispatch())
				display.sleep();
		}
	
	}
	
	/**
	 * Handle the situation when the window get closed
	 * In this case the window settings get saved
	 * @author Chris
	 */
	public void cleanup() {
	
		// Save the window information
		Point location = shell.getLocation();
		String x = "" + location.x;
		String y = "" + location.y;
		Properties appProps = Application.getInstance().getApplicationProperties(); 
		appProps.setProperty(ImageInformationWindow.PROPNAME_X, x);
		appProps.setProperty(ImageInformationWindow.PROPNAME_Y, y);

	}

	/**
	 * Get a hint how high a multi text entry field should be
	 * @return The height hint for a multi text entry field
	 * @author Chris
	 */
	protected int getEntryFieldHeightHint() {
		return 100;
	}
	
	/**
	 * Get a hint how wide a text entry field should be
	 * @return The width hint for a text entry field
	 * @author Chris
	 */
	protected int getEntryFieldWidthHint() {
		return 300;
	}
	
	/**
	 * Create a single text entry field
	 * @param Composite The parent where the text field shall be put into
	 * @param String The initial value of the text field
	 * @return The text field
	 * @author Chris
	 */
	protected Text createSingleTextField(Composite parent, String value) {
		
		// Create the text field
		final Text text = new Text(parent, SWT.SINGLE | SWT.BORDER);
		
		// Layout the text field
		GridData grid = new GridData();
		grid.grabExcessHorizontalSpace = true;
		grid.horizontalAlignment = GridData.FILL;
		grid.widthHint = getEntryFieldWidthHint();
		text.setLayoutData(grid);
		
		// Set the initial text
		text.setText(value);
		
		// Add a focus listener
		text.addFocusListener(
			new FocusListener() {
				public void focusGained(FocusEvent e) {
					((Text) e.widget).selectAll();
				}
				public void focusLost(FocusEvent e) {
				}
			}
		);
		
		// Return the text field
		return text;
	}
	
	/**
	 * Create a multi text entry field
	 * @param Composite The parent where the text field shall be put into
	 * @param String The initial value of the text field
	 * @return The text field
	 * @author Chris
	 */
	protected Text createMultiTextField(Composite parent, String value) {
		
		// Create the text field
		final Text text = new Text(parent, SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
		
		// Layout the text field
		GridData grid = new GridData();
		grid.grabExcessHorizontalSpace = true;
		grid.horizontalAlignment = GridData.FILL;
		grid.heightHint = getEntryFieldHeightHint();
		grid.widthHint = getEntryFieldWidthHint();
		text.setLayoutData(grid);
		
		// Set the initial value
		text.setText(value);
		
		// Add a focus listener
		text.addFocusListener(
			new FocusListener() {
				public void focusGained(FocusEvent e) {
					((Text) e.widget).selectAll();
				}
				public void focusLost(FocusEvent e) {
				}
			}
		);
		
		// Return the text field
		return text;
		
	}

	/**
	 * Create a label with the appropriate formatting
	 * @param Composite The parent where the text field shall be put into
	 * @param String The value of label
	 * @return The label
	 * @author Chris
	 */
	protected Label createLabel(Composite parent, String value) {
		
		// Create the label
		Label label = new Label(parent, 1);
		
		// Layout the label
		GridData grid = new GridData();
		grid.verticalAlignment = GridData.BEGINNING;
		label.setLayoutData(grid);
		
		// Set the label text
		label.setText(value);
		
		// Return the label
		return label;
		
	}

	/**
	 * Create the label for the title
	 * @param Composite The parent where the label should be put into
	 * @return The actual label
	 * @author Chris
	 */
	protected Label createTitleLabel(Composite parent) {
		return createLabel(parent, rb.getString("imageinfo_title") + ":");
	}
	
	/**
	 * Create the label for the gallery
	 * @param Composite The parent where the label should be put into
	 * @return The actual label
	 * @author Chris
	 */
	protected Label createGalleryLabel(Composite parent) {
		return createLabel(parent, rb.getString("imageinfo_gallery") + ":");
	}
	
	/**
	 * Create the label for the description
	 * @param Composite The parent where the label should be put into
	 * @return The actual label
	 * @author Chris
	 */
	protected Label createDescriptionLabel(Composite parent) {
		return createLabel(parent, rb.getString("imageinfo_description") + ":");
	}
	
	/**
	 * Create the label for the keywords
	 * @param Composite The parent where the label should be put into
	 * @return The actual label
	 * @author Chris
	 */
	protected Label createKeywordsLabel(Composite parent) {
		return createLabel(parent, rb.getString("imageinfo_keywords") + ":");
	}
	
	/**
	 * Create the label for the supplemental categories
	 * @param Composite The parent where the label should be put into
	 * @return The actual label
	 * @author Chris
	 */
	protected Label createSupplementalCategoriesLabel(Composite parent) {
		return createLabel(parent, rb.getString("imageinfo_supplementalcategories") + ":");
	}
	
	/**
	 * Create the label for the city
	 * @param Composite The parent where the label should be put into
	 * @return The actual label
	 * @author Chris
	 */
	protected Label createCityLabel(Composite parent) {
		return createLabel(parent, rb.getString("imageinfo_city") + ":");
	}
	
	/**
	 * Create the label for the state
	 * @param Composite The parent where the label should be put into
	 * @return The actual label
	 * @author Chris
	 */
	protected Label createStateLabel(Composite parent) {
		return createLabel(parent, rb.getString("imageinfo_state") + ":");
	}
	
	/**
	 * Create the label for the country
	 * @param Composite The parent where the label should be put into
	 * @return The actual label
	 * @author Chris
	 */
	protected Label createCountryLabel(Composite parent) {
		return createLabel(parent, rb.getString("imageinfo_country") + ":");
	}
	
	/**
	 * Create the label for the category
	 * @param Composite The parent where the label should be put into
	 * @return The actual label
	 * @author Chris
	 */
	protected Label createCategoryLabel(Composite parent) {
		return createLabel(parent, rb.getString("imageinfo_category") + ":");
	}
	
	/**
	 * Create the label for the copyright
	 * @param Composite The parent where the label should be put into
	 * @return The actual label
	 * @author Chris
	 */
	protected Label createCopyrightLabel(Composite parent) {
		return createLabel(parent, rb.getString("imageinfo_copyright") + ":");
	}
	
	/**
	 * Create the label for the date
	 * @param Composite The parent where the label should be put into
	 * @return The actual label
	 * @author Chris
	 */
	protected Label createDateLabel(Composite parent) {
		return createLabel(parent, rb.getString("imageinfo_date") + ":");
	}
	
	/**
	 * Create the text field for the title
	 * @param Composite The parent where the text field should be put into
	 * @return The actual text entry field
	 * @author Chris
	 */
	protected Text createTitleField(Composite parent) {
		iptc_title = getTitle();
		field_title = createSingleTextField(parent, iptc_title);
		return field_title;
	}
	
	/**
	 * Create the text field for the gallery
	 * @param Composite The parent where the text field should be put into
	 * @return The actual text entry field
	 * @author Chris
	 */
	protected Text createGalleryField(Composite parent) {
		iptc_gallery = getGallery();
		field_gallery = createSingleTextField(parent, iptc_gallery);
		return field_gallery;
	}
	
	/**
	 * Create the text field for the description
	 * @param Composite The parent where the text field should be put into
	 * @return The actual text entry field
	 * @author Chris
	 */
	protected Text createDescriptionField(Composite parent) {
		iptc_description = getDescription();
		field_description = createMultiTextField(parent, iptc_description);
		return field_description;
	}
	
	/**
	 * Create the text field for the keywords
	 * @param Composite The parent where the text field should be put into
	 * @return The actual text entry field
	 * @author Chris
	 */
	protected Text createKeywordsField(Composite parent) {
		iptc_keywords = getKeywords();
		field_keywords = createMultiTextField(parent, iptc_keywords);
		return field_keywords;
	}
	
	/**
	 * Create the text field for the supplemental categories
	 * @param Composite The parent where the text field should be put into
	 * @return The actual text entry field
	 * @author Chris
	 */
	protected Text createSupplementalCategoriesField(Composite parent) {
		iptc_supplementalcategories = getSupplementalCategories();
		field_supplementalcategories = createMultiTextField(parent, iptc_supplementalcategories);
		return field_supplementalcategories;
	}
	
	/**
	 * Create the text field for the city
	 * @param Composite The parent where the text field should be put into
	 * @return The actual text entry field
	 * @author Chris
	 */
	protected Text createCityField(Composite parent) {
		iptc_city = getCity();
		field_city = createSingleTextField(parent, iptc_city);
		return field_city;
	}
	
	/**
	 * Create the text field for the state
	 * @param Composite The parent where the text field should be put into
	 * @return The actual text entry field
	 * @author Chris
	 */
	protected Text createStateField(Composite parent) {
		iptc_state = getProvinceState();
		field_state = createSingleTextField(parent, iptc_state);
		return field_state;
	}
	
	/**
	 * Create the text field for the country
	 * @param Composite The parent where the text field should be put into
	 * @return The actual text entry field
	 * @author Chris
	 */
	protected Text createCountryField(Composite parent) {
		iptc_country = getCountry();
		field_country = createSingleTextField(parent, iptc_country);
		return field_country;
	}
	
	/**
	 * Create the text field for the category
	 * @param Composite The parent where the text field should be put into
	 * @return The actual text entry field
	 * @author Chris
	 */
	protected Text createCategoryField(Composite parent) {
		iptc_category = getCategory();
		field_category = createSingleTextField(parent, iptc_category);
		return field_category;
	}
	
	/**
	 * Create the text field for the copyright
	 * @param Composite The parent where the text field should be put into
	 * @return The actual text entry field
	 * @author Chris
	 */
	protected Text createCopyrightField(Composite parent) {
		iptc_copyright = getCopyright();
		field_copyright = createSingleTextField(parent, iptc_copyright);
		return field_copyright;
	}
	
	/**
	 * Create the text field for the date
	 * @param Composite The parent where the text field should be put into
	 * @return The actual text entry field
	 * @author Chris
	 */
	protected Text createDateField(Composite parent) {
		iptc_date = getDate();
		field_date = createSingleTextField(parent, iptc_date);
		return field_date;
	}
	
	/**
	 * Create the contents of the window which is the widgest strucutre,
	 * all input fields, labels etc.
	 * @param ApplicationWindow The main window of the application
	 * @param ImageContainer The selected image
	 * @return Shell The actual shell holding all widgets
	 * @author Chris
	 */
	protected Shell createContents(boolean readOnly) {

		// Create the shell
		final Shell shell = createShell();

		// Create the IPTC information group
		Group iptcGroup = new Group(shell, SWT.NONE);
		GridData iptcGridData = new GridData();
		iptcGridData.horizontalSpan = 2;
		iptcGroup.setLayoutData(iptcGridData);
		GridLayout iptcGridLayout = new GridLayout ();
		iptcGridLayout.marginWidth = 10;
		iptcGridLayout.numColumns = getGroupColumnsNumber();
		iptcGroup.setLayout(iptcGridLayout);
		
		// Create an additional widget if needed
		createAdditionalWidget(iptcGroup);
		
		// Create the title information
		createTitleLabel(iptcGroup);
		createTitleField(iptcGroup);
		
		// Create the gallery information
		createGalleryLabel(iptcGroup);
		createGalleryField(iptcGroup);
		
		// Create the description information
		createDescriptionLabel(iptcGroup);
		createDescriptionField(iptcGroup);
		
		// Create the keywords information
		createKeywordsLabel(iptcGroup);
		createKeywordsField(iptcGroup);

		// Create the city information
		createCityLabel(iptcGroup);
		createCityField(iptcGroup);

		// Create the state information
		createStateLabel(iptcGroup);
		createStateField(iptcGroup);

		// Create the country information
		createCountryLabel(iptcGroup);
		createCountryField(iptcGroup);

		// Create the category information
		createCategoryLabel(iptcGroup);
		createCategoryField(iptcGroup);

		// Create the supplemental category information
		createSupplementalCategoriesLabel(iptcGroup);
		createSupplementalCategoriesField(iptcGroup);

		// Create the copyright information
		createCopyrightLabel(iptcGroup);
		createCopyrightField(iptcGroup);

		// Create the date information
		createDateLabel(iptcGroup);
		createDateField(iptcGroup);

		if (readOnly)
		{
			for (int i=0;i<iptcGroup.getChildren().length;i++)
			{
				Control ctrl = iptcGroup.getChildren()[i];
				if (ctrl instanceof Text)
				{
					ctrl.setEnabled(false);
				}
			}
		}

		// Create the OK button
		Button okButton = new Button(shell, SWT.PUSH);
		GridData okButtonGridData = new GridData();
		okButtonGridData.verticalSpan = 8;
		okButtonGridData.horizontalAlignment = GridData.END;
		okButtonGridData.widthHint = 100;
		okButton.setLayoutData(okButtonGridData);
		okButton.setText(Application.getInstance().getResourceBundle().getString("button_ok"));
		okButton.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event event) {
				
				// Initialize the has changed variable
				boolean hasChanged = false;
				
				// Update the title if necessarz
				if (hasTitleChanged()) {
					hasChanged = true;
					setTitle(getNewTitle());
				}

				// Update the gallery if necessarz
				if (hasGalleryChanged()) {
					hasChanged = true;
					setGallery(getNewGallery());
				}
				
				// Update the description if necessarz
				if (hasDescriptionChanged()) {
					hasChanged = true;
					setDescription(getNewDescription());
				}

				// Update the city if necessarz
				if (hasCityChanged()) {
					hasChanged = true;
					setCity(getNewCity());
				}

				// Update the state if necessarz
				if (hasProvinceStateChanged()) {
					hasChanged = true;
					setProvinceState(getNewProvinceState());
				}

				// Update the country if necessarz
				if (hasCountryChanged()) {
					hasChanged = true;
					setCountry(getNewCountry());
				}

				// Update the category if necessarz
				if (hasCategoryChanged()) {
					hasChanged = true;
					setCategory(getNewCategory());
				}

				// Update the copyright if necessarz
				if (hasCopyrightChanged()) {
					hasChanged = true;
					setCopyright(getNewCopyright());
				}

				// Update the keywords if necessary
				if (hasKeywordsChanged()) {
					hasChanged = true;
					setKeywords(getNewKeywords());
				}

				// Update the supplemental categories if necessary
				if (hasSupplementalCategoriesChanged()) {
					hasChanged = true;
					setSupplementalCategories(getNewSupplementalCategories());
				}

				// Update the date if necessary
				if (hasDateChanged()) {
					hasChanged = true;
					setDate(getNewDate());
				}

				// Update the originator application if necessarz
				String originatingProgram = getOriginatingProgram();
				String newOriginatingProgram = Application.getInstance().getInternalProperties().getProperty(Application.PROPNAME_APPLICATION_NAME) + " " + Application.getInstance().getInternalProperties().getProperty(Application.PROPNAME_APPLICATION_VERSION);
				if (newOriginatingProgram.equals(originatingProgram) == false) {
					hasChanged = true;
					setOriginatingProgram(newOriginatingProgram);
				}

				// Write the image if necessary
				if (hasChanged == true) {
					save();
				}
				
				close();
			}

		});

		// Make it the default button
		shell.setDefaultButton(okButton);
			
		// Create the Cancel button
		Button cancelButton = new Button(shell, SWT.PUSH);
		GridData cancelButtonGridData = new GridData();
		cancelButtonGridData.verticalSpan = 8;
		cancelButtonGridData.horizontalAlignment = GridData.BEGINNING;
		cancelButtonGridData.widthHint = 100;
		cancelButton.setLayoutData(cancelButtonGridData);
		cancelButton.setText(Application.getInstance().getResourceBundle().getString("button_cancel"));
		cancelButton.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event event) {
				close();
			}
		});
	
		return shell;
		
	}
	
	/** 
	 * Close the window
	 * @author Chris
	 */
	protected void close() {
		cleanup();
		shell.dispose();
	}
	
	/**
	 * Create the shell that holds all the window widgest
	 * @return The actual shell holding all widgets
	 * @author Chris
	 */
	protected Shell createShell() {

		// Create the shell
		Shell shell = new Shell(mainWindow.getShell(), SWT.CLOSE | SWT.TITLE | SWT.APPLICATION_MODAL);
		GridLayout shellGrid = new GridLayout();
		shellGrid.marginWidth = 10;
		shellGrid.marginHeight = 5;
		shellGrid.numColumns = 2;
		shellGrid.makeColumnsEqualWidth = true;
		shell.setLayout(shellGrid);
		shell.setText (Application.getInstance().getResourceBundle().getString("title_image_information"));
		shell.setImage(ImageUtil.getImage("icons/logo.gif"));

		// Return the shell
		return shell;
		
	}
	
	/**
	 * Get the new title from the widget
	 * @return The new value from the widget
	 * @author Chris
	 */
	protected String getNewTitle() {
		return field_title.getText();
	}
	
	/**
	 * Get the new gallery from the widget
	 * @return The new value from the widget
	 * @author Chris
	 */
	protected String getNewGallery() {
		return field_gallery.getText();
	}
	
	/**
	 * Get the new description from the widget
	 * @return The new value from the widget
	 * @author Chris
	 */
	protected String getNewDescription() {
		return field_description.getText();
	}
	
	/**
	 * Get the new keywords from the widget
	 * @return The new value from the widget
	 * @author Chris
	 */
	protected String getNewKeywords() {
		return field_keywords.getText();
	}
	
	/**
	 * Get the new supplemental categories from the widget
	 * @return The new value from the widget
	 * @author Chris
	 */
	protected String getNewSupplementalCategories() {
		return field_supplementalcategories.getText();
	}
	
	/**
	 * Get the new city from the widget
	 * @return The new value from the widget
	 * @author Chris
	 */
	protected String getNewCity() {
		return field_city.getText();
	}
	
	/**
	 * Get the new state from the widget
	 * @return The new value from the widget
	 * @author Chris
	 */
	protected String getNewProvinceState() {
		return field_state.getText();
	}
	
	/**
	 * Get the new country from the widget
	 * @return The new value from the widget
	 * @author Chris
	 */
	protected String getNewCountry() {
		return field_country.getText();
	}
	
	/**
	 * Get the new category from the widget
	 * @return The new value from the widget
	 * @author Chris
	 */
	protected String getNewCategory() {
		return field_category.getText();
	}
	
	/**
	 * Get the new title from the widget
	 * @return The new value from the widget
	 * @author Chris
	 */
	protected String getNewCopyright() {
		return field_copyright.getText();
	}
	
	/**
	 * Get the new date from the widget
	 * @return The new value from the widget
	 * @author Chris
	 */
	protected String getNewDate() {
		return field_date.getText();
	}


	/**
	 * Return true if the title has changed, false otherwise
	 * @return True if the title has changed, false otherwise
	 * @author Chris
	 */
	protected boolean hasTitleChanged() {
		
		// If the field is not enabled, return false immediately
		if (!field_title.getEnabled()) return false;
		
		// Check whether or not the information changed
		return !getNewTitle().equals(iptc_title);
		
	}

	/**
	 * Return true if the gallery has changed, false otherwise
	 * @return True if the gallery has changed, false otherwise
	 * @author Chris
	 */
	protected boolean hasGalleryChanged() {
		
		// If the field is not enabled, return false immediately
		if (!field_gallery.getEnabled()) return false;
		
		// Check whether or not the information changed
		return !getNewGallery().equals(iptc_gallery);
		
	}

	/**
	 * Return true if the description has changed, false otherwise
	 * @return True if the description has changed, false otherwise
	 * @author Chris
	 */
	protected boolean hasDescriptionChanged() {
		
		// If the field is not enabled, return false immediately
		if (!field_description.getEnabled()) return false;
		
		// Check whether or not the information changed
		return !getNewDescription().equals(iptc_description);
		
	}

	/**
	 * Return true if the keywords have changed, false otherwise
	 * @return True if the keywords have changed, false otherwise
	 * @author Chris
	 */
	protected boolean hasKeywordsChanged() {
		
		// If the field is not enabled, return false immediately
		if (!field_keywords.getEnabled()) return false;
		
		// Check whether or not the information changed
		return !getNewKeywords().equals(iptc_keywords);
		
	}

	/**
	 * Return true if the supplemental categories have changed, false otherwise
	 * @return True if the supplemental categories have changed, false otherwise
	 * @author Chris
	 */
	protected boolean hasSupplementalCategoriesChanged() {
		
		// If the field is not enabled, return false immediately
		if (!field_supplementalcategories.getEnabled()) return false;
		
		// Check whether or not the information changed
		return !getNewSupplementalCategories().equals(iptc_supplementalcategories);
		
	}

	/**
	 * Return true if the city has changed, false otherwise
	 * @return True if the city has changed, false otherwise
	 * @author Chris
	 */
	protected boolean hasCityChanged() {
		
		// If the field is not enabled, return false immediately
		if (!field_city.getEnabled()) return false;
		
		// Check whether or not the information changed
		return !getNewCity().equals(iptc_city);
		
	}

	/**
	 * Return true if the state has changed, false otherwise
	 * @return True if the state has changed, false otherwise
	 * @author Chris
	 */
	protected boolean hasProvinceStateChanged() {
		
		// If the field is not enabled, return false immediately
		if (!field_state.getEnabled()) return false;
		
		// Check whether or not the information changed
		return !getNewProvinceState().equals(iptc_state);
		
	}

	/**
	 * Return true if the country has changed, false otherwise
	 * @return True if the country has changed, false otherwise
	 * @author Chris
	 */
	protected boolean hasCountryChanged() {
		
		// If the field is not enabled, return false immediately
		if (!field_country.getEnabled()) return false;
		
		// Check whether or not the information changed
		return !getNewCountry().equals(iptc_country);
		
	}

	/**
	 * Return true if the category has changed, false otherwise
	 * @return True if the category has changed, false otherwise
	 * @author Chris
	 */
	protected boolean hasCategoryChanged() {
		
		// If the field is not enabled, return false immediately
		if (!field_category.getEnabled()) return false;
		
		// Check whether or not the information changed
		return !getNewCategory().equals(iptc_category);
		
	}

	/**
	 * Return true if the copyright has changed, false otherwise
	 * @return True if the copyright has changed, false otherwise
	 * @author Chris
	 */
	protected boolean hasCopyrightChanged() {
		
		// If the field is not enabled, return false immediately
		if (!field_copyright.getEnabled()) return false;
		
		// Check whether or not the information changed
		return !getNewCopyright().equals(iptc_copyright);
		
	}

	/**
	 * Return true if the date has changed, false otherwise
	 * @return True if the date has changed, false otherwise
	 * @author Chris
	 */
	protected boolean hasDateChanged() {
		
		// If the field is not enabled, return false immediately
		if (!field_date.getEnabled()) return false;
		
		// Check whether or not the information changed
		return !getNewDate().equals(iptc_date);
		
	}

}

